Bayern, who moved up to third in the table, got off to a strong start, hitting the post through Thomas Muller before Robert Lewandowski's 19th league goal of the season gave them the lead in the 16th minute.
Freiburg, however, gradually gained control of the midfield, attacking with greater ease as Bayern were punished once more for their complacency.
Vincenzo Grifo volleyed in to level on the hour, completing a superb attacking move from the hosts, who missed several more scoring chances as the Bayern defence looked shaky.
Late substitute Zirkzee, the 18-year-old Netherlands youth international, then slipped into the box and slotted in from close range before Gnabry added another.
Bayern are third on 30 points, ahead of fourth-placed Borussia Dortmund on goal difference, while Borussia Monchengladbach joined RB Leipzig at the top of the table with a 2-0 win over Paderborn.
The Bundesliga will break after this weekend's action and will resume in mid-January following its traditional winter pause.
"It was a game with many ups and downs. Fortunately, with a happy finish in the end," Bayern interim coach Hans Flick told reporters. "At the end we had a bit of luck. Freiburg played well, they put us under pressure."
On his decision to bring on Zirkzee in stoppage time, Flick said he wanted the Dutchman to be in the mix should a cross be floated into the box.
"I thought if a cross comes in then he could put it in. He has been with us [from the reserves] for the past three weeks and his development is very noticeable."