Heartbreak for Uruguay as 3-2 win for the Netherlands ensures place in Sunday’s final.
Spain’s best previous World Cup finish were fourth in 1950, when the final round consisted of a four-team group.
In Sunday’s final at Soccer City, Spain will meet the Netherlands, which have also never won football’s biggest trophy.
Spain controlled throughout with their precision passing game, but had trouble finishing until Puyol’s late strike.
At the final whistle, the veteran 32-year-old raised his arms in triumph as goalkeeper Iker Casillas jumped on the centre back’s shoulders.
For much of the game, three-time champion Germany sat back and waited patiently to snuff out Spain’s attack, hoping to launch a counterattack that worked against England and Argentina but rarely took off in Durban.
Germany sorely missed suspended winger Thomas Mueller, who had scored four goals so far and was often able to outsprint opponents.
“In the last two or three years they have been one of the best and most united teams,” Germany coach Joachim Loew said. “They circulate the ball well and we couldn’t play the way we like to play.
“We had great plans but it didn’t work out.”
Loew seemed to think that Spain would also beat the Netherlands.
“They’ve won all the big games in the last year. It’s a good team that has played together now for two or three years,” the Germany coach said.
“I am sure the Spanish can win any game because they are dominant and it’s hard to contain their attack.”
Spain, which had 13 shots on goal to Germany’s five, had never beaten Germany at the World Cup in three previous attempts.
“From defence through to attack I think we played a great game,” Spain coach Vicente del Bosque said. “We’ve got another game in front of us, let’s see if we are able to control the ball. We’re in good shape physically, so let’s see if we can win.”
At Euro 2008, Spain broke a 44-year major title drought, and the team have lost only two games since November 2006 – the last coming in their World Cup opener against Switzerland.
“We’ve shown that in the big moments we can grow even more,” Villa said.
The match got an early jolt when a fan carrying a vuvuzela ran on to the field four minutes in – suspending the match briefly until security officers forcibly removed the intruder.
Out of form Liverpool forward Fernando Torres – who has not scored in this tournament – was dropped from Spain’s lineup and Pedro Rodriguez was given his first national team start, complementing Villa in attack.
Spain set the tone early with its quick passing and Pedro set up Villa with a well-placed through ball in the sixth minute, but Germany goalkeeper Manuel Neuer did well to leave his net and block Villa’s shot.
In the 13th, Spain threatened again when a dangerous header by Puyol from the edge of the box went over the bar.
With Spanish players swarming around in bunches in midfield, Germany struggled to launch their counterattack.
The first serious threat from Germany did not come until the 32nd minute when Casillas dived to his left to push wide a long, low shot from Piotr Trochowski.
Germany playmaker Mesut Oezil strode through the centre of Spain’s defence in first-half injury time but was muscled off the ball by Sergio Ramos.
Hungarian referee Viktor Kassai turned down penalty appeals.
The second half started much like the first, with Spain taking the initiative.
Xabi Alonso sent a long shot wide in the 48th, then pulled in a little closer for another more dangerous attempt two minutes later, forcing Neuer to dive to his left, but the ball again sailed wide.
In the 58th, Spain produced two more chances – one right after the other.
First, Neuer saved an attempt from Pedro, then Andres Iniesta slid a cross through the box but Villa could not reach it for what would have been a sure goal.
Germany hardly even approached Spain’s area until Lukas Podolski passed to second-half substitute Toni Kroos in the area in the 69th and Casillas stopped Kroos’ shot by the post.
Torres replaced Villa for the final nine minutes and immediately made an impact, sending Pedro striding upfield in the 82nd.
But Pedro refused to give the ball back to Torres, wasting a chance to double Spain’s lead.
Germany will face Uruguay in the third-place match on Saturday in Port Elizabeth.
“Tomorrow, we’ll have to try and encourage the team,” Loew said. “Because there is one game left to play.”