Edin Dzeko bagged an early brace, Stephan El Shaarawy punished his former club and Daniele De Rossi scored a late penalty as Roma ran riot away to Milan.
This was a decisive match for both sides, as the Rossoneri could break away from the chasing pack to consolidate the Europa League spot, while the Giallorossi needed a victory to recapture second place from Napoli. Mattia De Sciglio and Jose Sosa returned from suspension, but Juraj Kucka was banned with Davide Calabria, Alessio Romagnoli, Luca Antonelli, Ignazio Abate, Giacomo Bonaventura and Andrea Poli injured. Gianluca Lapadula was preferred to Carlos Bacca for the third game running. Kevin Strootman and Antonio Rudiger sat out bans following a fiery Derby della Capitale defeat to Lazio.
The Giallorossi won here 3-1 last season, so this was their second consecutive success at the Stadio Giuseppe Meazza.
It took just eight minutes to break the deadlock at San Siro, as Mohamed Salah pressed Leonel Vangioni into giving up possession, completed a double give-and-go with Edin Dzeko, the Bosnian smashing into the near top corner. It was his 26th Serie A goal of the season, making him sole Capocannoniere.
Half of the stadium thought Gerard Deulofeu had equalised, but his shot was deflected on to the wrong side of the netting.
Roma were very dangerous on the counter and Diego Perotti’s stunning curler from 25 metres was fingertipped on to the far post by Gianluigi Donnarumma in an astounding save.
A poor Vangioni clearance fell to Dzeko, who snatched at his volley over the bar, but Wojciech Szczesny risked being propelled back into his own net by a Jose Sosa missile from distance.
Dzeko added another goal with his precise header at the near post from a Leandro Paredes corner, out-jumping the Milan defenders. It made him the second all-time Roma scorer in a single Serie A season, behind Rodolfo Volk on 29 in 1930-31.
Szczesny nudged a corner just over Cristian Zapata and Perotti hit the side-netting after a Salah back-heel flick. Radja Nainggolan thought he had scored on the counter with a daisy-cutter from the edge of the box, but Donnarumma again got a fingertip to turn it on to the upright and Salah blasted the follow-up over.
The 18-year-old goalkeeper was decisive once more, rushing off his line to block the angle when Nainggolan threaded through for Perotti on the counter. Roma had a remarkable 17 shots on goal in the first half.
Ex-Roma midfielder Andrea Bertolacci came on for the second half, but the visitors seemed content to sit on their lead and invite Milan forward, ready to go on the counter.
Donnarumma smothered an Emerson Palmieri volley and Bertolacci blasted wide, but Nainggolan went off with a calf problem.
Stephan El Shaarawy came on against his former club and the curler was arrowing its way into the far bottom corner before Donnarumma got down to beat it away at full stretch.
Just as Milan seemed to be losing belief, a corner was knocked on by Lucas Ocampos for the Mario Pasalic diving header at the back post.
The Rossoneri have fought back from 2-0 down on several occasions this season, but the hope was short-lived, as El Shaarawy curled into the far top corner from just inside the penalty area. The Little Pharaoh refused to celebrate against his former teammates.
Salah almost got a fourth, combining with Clement Grenier for a shot deflected over. Dzeko went off with cramp, so there was no final run-out at San Siro for Francesco Totti, even though the Milan fans unveiled a banner honouring the Roma legend.
There was still time for another Giallorossi goal, as Salah sprung the offside trap and Gabriel Paletta brought him down with a combination of shirt-tug and heel-clip. Daniele De Rossi’s penalty was low and Donnarumma got his hand to it, but the ball bounced up and over him. Paletta received his fourth red card of the season.
Sent off: Paletta 86 (M)