Atletico Madrid snatched a place in the Champions League last 16 as they won 3-1 at Porto in a tense and ill-tempered game which saw both sides have a man sent off.
Antoine Griezmann put Atletico in front against the run of play in the 56th minute, bundling the ball into the net following a corner after the hosts had missed a glut of clear chances either side of the break.
Atletico's lead was threatened when Yannick Carrasco was sent off in the 67th minute for raising his hands to Otavio as tempers raged following a foul but Porto's numerical advantage lasted only a few moments as Wendell was dismissed for appearing to lightly strike Matheus Cunha with his elbow.
Substitute Angel Correa clinched the win for Atletico with a cold-blooded finish on the counter-attack in the 90th minute, while Rodrigo de Paul added a third goal 60 seconds later.
Porto then earned a consolation penalty which Sergio Oliveira converted with the final kick of the game.
Real Madrid finished top of Group D as goals from Toni Kroos and Marco Asensio secured a 2-0 home win over Inter Milan who had Nicolo Barella sent off.
Both teams had already booked their last-16 places and Real ended on 15 points from six games, five more than second-placed Inter.
Kroos struck the opener in the 17th minute with a crisp low shot from 20 metres which flashed past goalkeeper Samir Handanovic, the 1,000th goal scored in the European Cup and Champions League by the record 13-times champions.
Inter were on the back foot and substitute Asensio made it 2-0 with a goal of the highest quality in the 79th minute shortly after coming on for Rodrygo. Crisp one-touch passing left the Spanish forward with time and space on the edge of the penalty area to curl a delightful shot which flew into the back of the net off the post.
Sheriff Tiraspol ended their Champions League campaign by avoiding a fourth successive defeat when Boban Nikolov's last-gasp strike earned them a 1-1 draw at Shakhtar Donetsk in Group D.
The Moldovan side looked to be heading towards another loss after Fernando's opening goal three minutes before halftime, only for Nikolov to salvage a point three minutes into stoppage time with a shot from close range.
Sebastien Haller kept up his scoring streak with a well taken penalty to help Ajax Amsterdam end Group C with a 100% record as they beat Sporting Lisbon 4-2 at the Johan Cruyff Arena.
Sporting equalised through Nuno Santos in the 22nd minute but Brazilian Antony restored the Dutch side's lead just before halftime after a horror defensive error by the visitors.
David Neres extended Ajax’s advantage after 58 minutes and four minutes later Steven Berghuis added the fourth, but Sporting got a 78th minute consolation with Bruno Tabata’s thunderous side-footed shot off the underside of the crossbar.
Borussia Dortmund's Marco Reus and Erling Haaland each scored twice in a 5-0 win over Besiktas in their final Group C game, with the Germans already confirmed in third place.
Reus scored with a first-half stoppage time penalty and added another with a superb solo effort while Haaland headed in two more goals after coming on as a substitute, as Dortmund ended the campaign on a high and will continue in the Europa League.
Already-qualified Paris St Germain finished the group stage in style as Kylian Mbappe and Lionel Messi bagged doubles in a 4-1 home win over Club Brugge.
France forward Mbappe put the Ligue 1 side 2-0 up in the first seven minutes and Messi added a third before the break plus a fourth with a penalty after the interval.
PSG were guaranteed second place in Group A before kickoff while Manchester City, who lost 2-1 at RB Leipzig on Tuesday, had already secured top spot and finished with 12 points, one ahead of Mauricio Pochettino's Paris side.