Bottas had an advantage of 0.231s over Hamilton after the first runs in Q3 but was unable to improve on his second set of Pirellis softs.
Hamilton then put in a lap of 1m10.166s to outpace Bottas by 0.086s, with his stunning pace through the first sector crucial to his advantage.
Bottas held on to second place as Red Bull driver Max Verstappen was also unable to improve and stayed in third place, 0.475s down.
Sebastian Vettel made a promising start to his lap and looked set to improve, but kissed the wall at the exit of the Tabac right-hander and had to settle for fourth based on his first-run time.
Red Bull driver Pierre Gasly was fifth, 0.875s off the pace, but faces a post-session investigation for impeding Haas driver Romain Grosjean during Q2.
Kevin Magnussen was best of the rest in sixth place and was the only driver outside the top three teams to still have enough tyres for two runs using fresh Pirelli softs.
Having taking sixth place on his first run, he then briefly lost it to Renault driver Daniel Ricciardo, before reclaiming it on the second run.
Toro Rosso driver Daniil Kvyat was eighth fastest, a tenth and a half quicker than McLaren driver Carlos Sainz.
Teammate Alex Albon made Q3 for the first time in his F1 career, but had to settle for 10th and 1.487s off the pace.
Renault driver Nico Hulkenberg was bumped out of the top 10 in the final moments of Q2 when Magnussen, who had struggled on his first run after locking up and clipping the inside wall at Mirabeau, improved.
Lando Norris was half-a-tenth behind Hulkenberg in 12th place and 0.3s quicker than Grosjean – who complained about traffic over the radio at the end of the session after being impeded by Gasly.
Alfa Romeo pairing Kimi Raikkonen and Antonio Giovinazzi were 14th and 15th respectively, with just 0.070s separating the pair.
Charles Leclerc was the quickest of those to fall in Q1 in 16th place after completing just one run and failing to make the top 15 by 0.052s despite being only 0.715s off the pace in the session.
Leclerc had posted a best time of 1m12.149s but was not sent back out despite picking up a flat spot on his soft Pirellis during that run as Ferrari felt he was safe.
Leclerc also initially missed the weighbridge when he returned after his run, although was pushed back by the Ferrari team before entering the garage and confirmed he had enough fuel and time to have completed a second run after the time lost to this.
Albon, Hulkenberg and teammate Vettel then ahead of him on their final laps of Q1 to ensure he didn’t make the cut.
Vettel himself had not set a strong enough time on his first run having abandoned the final quick lap on his first run after kissing the wall at the exit of the first left/right at Swimming Pool.
This meant he was among those at risk of elimination before improving on the only fast lap he had time for on his second set of tyres.
Racing Point duo Sergio Perez and Lance Stroll were 17th and 18th, with 0.613s separating the pair.
George Russell prevailed in the battle of the Williams drivers to take 19th place, with Robert Kubica bringing up the rear after lapping 0.274s slower.