World champion Sagan, in the green jersey, was always well placed as the final run to the line started and once the Bora-hansgrohe man kicked he could not be brought back, beating Sonny Colbrelli (Bahrain-Merida) to the line.
Philippe Gilbert (QuickStep Floors) had put in a huge dig with more than 700m remaining, but the Classics hardman could not shed himself of his rivals as he had to settle for third.
Yellow jersey Greg van Avermaet was seventh, meanwhile, and remains the race leader having also picked up a two-second time bonus earlier in the stage.
Drama on the undulating finale was always likely but the start proved lively too – after Michael Matthews (Team Sunweb) and Tiesj Benoot (Lotto-Soudal) were unable to take the start this morning, Robert Kiserlovski (Katusha-Alpecin) joined them on the abandoned list after crashing with Gianni Moscon inside the first 5km. However, Moscon, a key domestique for Team Sky, was ok to continue.
Seven riders eventually got into the breakaway, with plenty of firepower between them: Julien Vermote (Dimension Data), Nicolas Edet (Cofidis), Elie Gesbert (Fortuneo-Samsic), Jasper de Buyst (Lotto-Soudal), Toms Skujins (Trek-Segafredo) and Direct Energie duo Sylvain Chavanel and Lilian Calmejane.
Chavanel led the way through the intermediate sprint as the seven-strong group built up a four-minute lead, while stage four winner Fernando Gaviria temporarily closed the gap to Sagan in the green jersey by winning the jump from the peloton.
Chavanel outlined his ambitions further on the day’s early climbs, going solo at the front as he hoovered up the first four climbing points.
After the Cote de la Roche du Feu, Chavanel was joined by Skujins, Edet and Calmejane but Gesbert crashed heavily into the grass after a momentary loss of control while descending in an aero tuck.
Back in the bunch, Chris Froome suffered a mechanical which required a bike change and chase back to the bunch, while Mark Cavendish was one of the first riders spat out the back as the climbs bit – Marcel Kittel and Dylan Groenewegen joining him in the grupetto.
Stage five analysis
Second on stages one and four, winner on stages two and five. Peter Sagan is in top form and it's going to take some effort to wrestle that green jersey from him.
The GC men stayed safe in a tricky finale, which means we can focus on the stage winner for once - and once Sagan kicked he was never going to lose. Should Philippe Gilbert have kept his powder dry instead of dropping the hammer with 700m to go? Possibly, but it's hard to see anyone stopping Sagan on finishes like this on this form.
Another crash in the peloton, this time affecting Mikel Nieve (Mitchelton-Scott) among others – though all riders remounted – caused splits to appear, but the chase remained controlled with the break around two minutes in front.
Chavanel dropped back and Skujins led over the next two climbs to undo the Frenchman’s efforts and claim the polka-dot jersey, but the gap was coming down fast.
Indeed, the peloton caught the escapees in sight of the bonus sprint, allowing Julian Alaphilippe (QuickStep Floors) to snaffle three bonus seconds and yellow jersey Van Avermaet to extend his lead with two bonus seconds.
A lull in proceedings followed, allowing Rein Taaramae (Direct Energie) to escape, but he was soon swept up as all the stage favourites gathered towards the front.
Gilbert went long but his effort proved to be too much, too soon and it was left to Van Avermaet to lead the sprint out instead – but once Sagan burst past, a second stage win for the Slovakian was never in doubt.
Tour de France 2018: stage five – result
1) Peter Sagan (SVK) – Bora-hansgrohe – 4.48.06hrs
2) Sonny Colbrelli (ITA) – Bahrain-Merida – ST
3) Philippe Gilbert (BEL) – QuickStep Floors
4) Alejandro Valverde (ESP) – Movistar
5) Julian Alaphilippe (FRA) – QuickStep Floors
6) Daniel Martin (IRL) – UAE Team Emirates
7) Greg van Avermaet (BEL) – BMC Racing
8) Soren Kragh Anderson (DEN) – Team Sunweb
9) Andrea Pasqualon (ITA) – Wanty-Groupe Gobert
10) Vincenzo Nibali (ITA) – Bahrain-Merida
1) Greg van Avermaet (BEL) – BMC Racing – 18.22.00hrs
2) Tejay van Garderen (USA) – BMC Racing +2”
3) Philippe Gilbert (BEL) – QuickStep Floors +3”
4) Geraint Thomas (GBR) – Team Sky +5”
5) Julian Alaphilippe (FRA) – QuickStep Floors +6”
6) Bob Jungels (LUX) – QuickStep Floors +9”
7) Tom Dumoulin (NED) – Team Sunweb +13”
8) Soren Kragh Andersen (DEN) – Team Sunweb – ST
9) Rigoberto Uran (COL) – EF-Drapac +37”
10) Rafal Majka (POL) – Bora-hansgrohe +52”
12) Richie Porte (AUS) - BMC Racing +53"
13) Alejandro Valverde (ESP) - Movistar +55"
14) Mikel Landa (ESP) - Movistar - ST
15) Chris Froome (GBR) - Team Sky +57"
16) Adam Yates (GBR) - Mitchelton-Scott +1.02
17) Vincenzo Nibali (ITA) - Bahrain-Merida +1.08
18) Romain Bardet (FRA) - Ag2r-La Mondiale +1.17
33) Nairo Quintana (COL) - Movistar +2.10