Ricardo Quaresma scored twice in the opening 10 minutes — the first a penalty — thanks to gifts from Bayern, and although the Germans got a goal back through Thiago Alcantara in the 27th minute, Jackson Martinez capped a fine night for Porto in the 65th.
The result sees Porto extend their unbeaten record throughout this season’s Champions League, and Bayern must find a way to end it in Germany if they are to reach the semi-finals for a fourth consecutive season.
The match exploded into life when Porto were awarded a penalty with barely a minute gone.
Xabi Alonso was caught napping on the edge of the area and Jackson Martinez nicked the ball off him before being brought down by Manuel Neuer, who could be grateful for a covering defender as he was only booked.
Even so, Quaresma sent the Bayern goalkeeper the wrong way from the penalty spot and the hosts led.
Bayern were slow to get going but had a chance from an eighth-minute free kick as the ball bounced up for Robert Lewandowski, but his header went over as he struggled to deal with the bounce.
Seconds later, Porto doubled their lead thanks to another defensive mistake, with Dante this time at fault as Quaresma stole the ball away and poked it past Neuer.
Bayern looked stunned but stayed calm and sought to play their way back into the match, quickly establishing control of possession.
They got their reward in the 27th minute when Jerome Boateng sent in a low cross which somehow got through a crowd of players before Thiago, with the ball behind him at the far post, hung out a leg and hooked it in.
Porto had a response, and Alex Sandro almost got lucky when Neuer had to push his floating ball onto the crossbar before Casemiro headed over when Neuer failed to deal with a free-kick.
Porto looked strong as the second half started and continued to threaten.
Casemiro tried to catch Neuer off his line with a long-range effort before the Bayern goalkeeper made a brilliant save to keep out Hector Herrera’s close-range effort.
But the third goal came in the 65th minute thanks to yet another defensive howler as Boateng misjudged a deep ball, allowing it to bounce and Martinez swept it up before rounding Neuer and finishing from a tight angle.
Bayern had no answer and Pep Guardiola’s side have it all to do in the second leg.
Fabiano, Danilo, Maicon, Martins Indi, Alex Sandro, Herrera, Casemiro, Torres (Ruben Neves 75), Quaresma (Evandro 85), Martinez, Brahimi (Hernani 79).
Neuer, Rafinha, Boateng, Dante, Bernat, Lahm, Alonso (Badstuber 74), Thiago, Muller, Lewandowski, Gotze (Rode 57).
Carlos Velasco Carballo (Spain).