A solitary goal decided the second leg, as Iker Casillas put through his own net, but the damage had been done in Dortmund a week previous.
The hosts never really offered enough in response and Thomas Tuchel's side strode through without any real hassle.
Casillas' own-goal kills the tie
Despite needing to score three goals to go through, Porto were reluctant to come forward in the early stages and force the breakthrough required. They had the momentum, but made very little of it. Only Henrikh Mkhitaryan's early shot, which was easily dealt with by Casillas, signaled any intent from either side.
Midway through the half, the opening goal came. A lovely move from Dortmund created the opener, although there was some controversy to go with it.
Ilkay Gündogan unleashed Mkhitaryan on the right, who whipped in a great cross to Marco Reus. He volleyed at goal and Casillas made a tremendous save, though the rebound fell to Pierre-Emerick Aubameyang. The Gabonese seemed certain to score but hit the bar from a few yards out; the Spanish stopper brought it over his own line, however.
Despite Porto protests for an offside which should have been given against Aubameyang, Mark Clattenburg gave the goal. That seemed to spark some fight in the hosts, who went close through Evandro, Marenga and Varela. A combination of near misses and Roman Bürki came to BVB's rescue.
Second half fails to excite
Tuchel brought on Nuri Sahin and Neven Subotic at the break, in order to build up their match fitness. The changes may have played a role in Vincent Aboubakar having a great chance to level after a fine pass from Marenga, though the forward couldn't apply the necessary finish.
Aubameyang and Mkhitaryan had the ball in the net in the second half but both were ruled out for offside. Yacine Brahimi hit the post and Mkhitaryan the bar, in what was an unlucky game for the Armenian, but the job was done. BVB booked their place in the last 16 with a 0-1 win, with Porto resigned to domestic competition for the remainder of the season.