Ferrari’s Fernando Alonso took a fighting second and Rosberg, who had started on pole position looking like a runaway winner, finished fourth and saw his 14 point lead over Hamilton trimmed to 11.
The German was caught out by two safety car interventions that turned the race on its head – and also by Hamilton refusing to give anything away despite being asked controversially to let him through.
With eight races to go, and double points for the last in Abu Dhabi, Rosberg has 202 points to Hamilton’s 191. Mercedes have a 174-point lead over champions Red Bull in the constructors’ standings.
Ricciardo, a revelation in his first year at Red Bull, bellowed in elation after he took the chequered flag 5.2 seconds ahead of Alonso for his second win of the season and his career.
“It feels as good as the first, it really does,” beamed the Australian, who was triumphant in Canada in June, after a rollercoaster of a race packed with incident.
“The safety car at the beginning played to our advantage…when the second one came out it didn’t really help us, but we managed to pull it off at the end.”
All of the top four had led over the course of a race that started after a brief downpour, was interrupted by two big crashes, continued with the ever-present threat of rain and finished on a knife-edge.
Just 6.3 seconds separated the top four, with Hamilton crossing the line just half a second clear of Rosberg.
Ricciardo, who had led earlier in the race and was on fresher tyres, swept past Hamilton three laps from the end and then overtook a determined Alonso to regain the lead for good.
“We took a gamble. We risked today just trying to get the victory and we went very close,” said Alonso of his second podium finish of the season.
“We need some crazy races to get some podiums and today we took the opportunity.”
Behind them, Rosberg – who made three stops to Alonso and Hamilton’s two – was closing in remorselessly and was all over the back of his team mate’s car as they started the final lap.
Hamilton hung on for a podium finish that had looked improbable on Saturday, but he was unhappy with his team for asking him to let Rosberg through with a third of the race remaining.
The German had yet to make his final stop, whereas Hamilton had done his final tyre change already and knew Rosberg was sure to come back at him with a vengeance in the closing stages.
“I’m not letting him past me, if he gets close enough to overtake he can overtake,” Hamilton had said over the radio.
In the end Rosberg did not get close enough to pass and eventually pitted.
“Just because he had one more stop than me doesn’t mean I am not in same race,” Hamilton told reporters later. “I was very, very shocked that the team would ask me to do that.
“But to be honest he didn’t get close enough to overtake. I was never going to lift off and lose ground to Fernando or Daniel to enable him to have a better race.”
Hamilton, who had won for the last two years in Hungary and was chasing a record fifth win in the country, had finished third in Germany last weekend after starting 20th and he said he was pushing as hard as he could to see where he ended up.
The Briton started from the pitlane, behind Danish rookie Kevin Magnussen whose McLaren was also moved off the grid, after his car caught fire in qualifying without completing a lap.
He spun at the second corner, skimming the wall with his front wing, and was 21 seconds down on Rosberg after the first lap with a mountain to climb.
By lap seven Hamilton was up to 14th and events played into his hands when the safety car was deployed after Sweden’s Marcus Ericsson crashed his Caterham at turn three.
While the top four decided not to pit, in what proved out to be a game changer, Hamilton and others did. Ricciardo then took the lead when they came in a lap later.
Frenchman Romain Grosjean kept the safety car out for four more laps when he crashed his Lotus on lap 11 and there was a further interruption on lap 23 when Force India’s Mexican Sergio Perez speared into the wall on the pit straight.
Brazilian Felipe Massa was fifth for Williams while Ferrari’s Kimi Raikkonen raced from 15th to sixth and his highest finish of a disappointing season so far.
Quadruple world champion Sebastian Vettel, who spun and was lucky not to emulate Perez in crashing into the wall, was seventh after starting on the front row.
Finland’s Valtteri Bottas took seventh, ahead of Frenchman Jean-Eric Vergne for Toro Rosso and McLaren’s Jenson Button.
Sauber failed to score points for the 11th race in a row while Germany’s Nico Hulkenberg crashed and drew a blank for the first time this season.
(Editing by Ed Osmond/Tony Goodson)