Barca had the chance to inflict a third consecutive defeat on their rivals and increase the pressure on Zinedine Zidane.
Instead, Madrid's win earns them a six-point lead over the Catalans, having played one game more.
"They are only three points but we have to enjoy them, especially after everything that has been said about the squad," said Zidane.
Sergio Ramos' penalty and a late Luka Modric goal finished Barcelona off after Federico Valverde and Ansu Fati had traded early strikes in a compelling game that defied those expecting further evidence of two heavyweights in decline.
"You always have bad spells in a season and hopefully this one only lasted a week," said Ramos. "To win in the home of your oldest rivals is always satisfying."
Atletico Madrid took advantage by beating Real Betis 2-0 to move up to second in the table but Sevilla missed out, slipping to a surprise 1-0 loss at home to Eibar.
Barcelona might feel hard done by, especially as Ramos' penalty was awarded after a check by VAR and a hugely exaggerated fall by Madrid's captain after Clement Lenglet tugged his shirt.
"Maybe one day you can explain how VAR works here in Spain," said Ronald Koeman. "We've had five games in the league and VAR has only intervened against Barca."
But aside from an impressive spell in the first half, when Lionel Messi threatened to win the game on his own, Madrid were dominant for longer spells and pulled away when it mattered.
This was Koeman's first Clasico as Barcelona coach and some of the pressure Zidane would have felt from a loss is now transferred to the Dutchman, who has overseen only three wins from his first six games in charge.
Barcelona face Juventus on Wednesday in the Champions League, without the suspended Gerard Pique.
Every Barca set-back now reflects on the future of Messi, for whom this could even have been the last Clasico at Camp Nou. The Argentinian, who failed in his attempt to leave last summer, has still to score from open play this season.
Suarez on target
Mundo Deportivo had declared the match "The least Clasico of Clasicos" on its front page on Saturday morning.
The coronavirus pandemic meant the biggest football stadium in Europe was silent for the game that La Liga president Javier Tebas had claimed is "still the biggest football match in the world".
In the previous week, Real Madrid had lost to Shakhtar Donestsk and promoted Cadiz, while Barcelona were beaten by Getafe.
Yet a dip in quality does not have to mean a drop in drama and any doubts about the thrill of this fixture were answered in eight minutes, as both teams had scored.
Madrid made the start they dreamed of when Karim Benzema drifted deep into the right channel and was allowed to turn, with the sprinting Valverde going beyond him.
Valverde pierced the gap before lifting a curved shot past Neto and into the far corner.
But within three minutes, Barcelona were level. Messi, from almost left-back, chipped a ball over the top freeing Jordi Alba, who fired into the front post where Fati had nipped ahead of Ramos and turned in.
It was fast and open for the rest of the half, with Messi enjoying a golden spell. He glided past Casemiro and then swerved around Ramos, but onto his right foot, the finish stabbed into Thibaut Courtois at the near post.
Benzema should have scored too and Madrid finished the stronger before half-time but Barca were better just after. Fati flashed wide and Philippe Coutinho missed a free header at the back post.
Their momentum was checked by Lenglet's error, a tug in the box clear enough that Ramos' shirt was stretched, even if the fall was comical. Referee Juan Martinez checked the monitor. Ramos found the corner.
Barca wanted a penalty themselves when Ramos booted a clearance into the arm of Raphael Varane but this time there was no whistle and Madrid were hardly troubled thereafter.
Ramos and Valverde both could have made it three in the final minutes before substitute Modric did, finishing beautifully after Neto rushed out.
Atletico are only a point behind their city rivals and have a game in hand after goals from Marcos Llorente and Luis Suarez saw off Betis, who had Martin Montoya sent off with 16 minutes left.