Lazio had the better of the first half and hit the woodwork, but Milan snatched a precious victory to retain fourth place after a Franck Kessie penalty.
This was a head-to-head for that fourth and final Champions League spot, although neither side was in good form going in, with Roma and Atalanta providing stiff competition. Gianluigi Donnarumma, Lucas Paqueta and Jack Bonaventura were still out of action, so Fabio Borini was again chosen to complete the trident attack. The visitors, who have a game in hand to be played against Udinese, missed Stefan Radu, Adam Marusic, Valon Berisha, Jordan Lukaku and suspended Milan Badelj.
Lazio were without a Serie A victory away to Milan since a Paolo Maldini own goal in 1989, followed by 11 draws and 17 defeats. These sides drew 0-0 in Rome for the first leg of the Coppa Italia semi-final and the decider will be at San Siro on April 24.
There was a huge chance for Lazio to take the lead within three minutes, as Joaquin Correa dribbled into the box, rolled across for Ciro Immobile, whose scuffed finish forced a desperate fingertip save from Pepe Reina.
Alessio Romagnoli had to time his sliding tackle on Correa perfectly when the Lazio man was clear on goal, while Reina smothered a Luis Alberto curler and Correa volleyed over from a Ciro Immobile pull-back.
The first genuine Milan chance was a counter-attack, as Hakan Calhanoglu’s snapshot was deflected wide, while Suso fired just over after good work from Borini and Krzysztof Piatek.
On the half-hour mark, Piatek thought he’d scored with a wonderful header on Davide Calabria’s cross, steering it back across the face of goal to shave a coat of paint off the upright.
Calhanoglu’s powerful strike was fingertipped over by Thomas Strakosha, but on the stroke of half-time Immobile gathered a through ball, spun round and clipped the outside of the near post.
Correa went off with a foot injury straight after the restart and Lucas Leiva’s volley from a corner hit the side-netting, while Reina spilled a Romulo shot from a tricky angle.
There was a double blow for Milan, as they simultaneously lost Alessio Romagnoli and Davide Calabria to injury. Cristian Zapata and Diego Laxalt came on, and immediately the Brazilian centre-back went on a mazy run from midfield, threaded through for Piatek and Strakosha had to come rushing out to block.
There was a bizarre couple of minutes when the referee awarded two penalties to Milan. The first was for handball, but after viewing on VAR, he acknowledged Francesco Acerbi’s arm was across his torso and giving no advantage, so changed his mind.
Within seconds of the restart, Gianluca Rocchi had pointed to the spot again, this time for a clumsy and easily avoidable Riza Durmisi shoulder-charge on Mateo Musacchio. Franck Kessie sent Strakosha the wrong way to convert and give Milan the lead.
Suso’s curler from distance was swerving into the far top corner, but Strakosha flew for a decisive one-handed save to claw it away.
Lazio had penalty appeals in stoppages, as Ricardo Rodriguez seemed to just about get the ball before Sergej Milinkovic-Savic, although another camera angle wasn’t quite so clear.
At the final whistle, a mass brawl broke out between both sets of teams, though the main battle seemed to be between Luiz Felipe and Borini.
See the latest Serie A predictions and betting tips with Eurotips.co.uk