Webb (15-10), who had lost his last two after winning six in a row, gave up two runs, including Jim Edmonds' solo homer, on four hits in six innings. He fanned five and walked four.
Jose Valverde pitched the ninth for his major-league best 43rd save in 50 chances.
Eric Byrnes was 2-for-4 including a double that drove in the tying run from first in Arizona's two-run sixth. The Diamondbacks went on to score the go-ahead run when second baseman Aaron Miles tripped on what should have been the inning-ending double play.
Jeff Salazar started the inning with a walk from Adam Wainwright, who lost for the first time in five starts. Wainwright (13-10) gave up three runs, two earned, on six hits in six innings.
After Salazar walked, Mark Reynolds reached on an infield single despite a diving stop by shortstop David Eckstein. Chris Young bounced to Miles in what looked to be an easy double play. But the second baseman tripped just before he got to the bag and couldn't get the throw off to first. Byrnes scored to put Arizona up 3-2.
The Cardinals threatened to tie in the eighth when Albert Pujols led off with a double and moved to third when Edmonds flew out to center. But after Chris Duncan walked, Brandon Lyon struck out Russell Branyan, then Yadier Molina bounced out to short to end the inning.
Tony Clark's home run in the eighth off Ryan Franklin made it 4-2.
The Diamondbacks scored an unearned run in the second when Augie Ojeda, starting in place of injured Orlando Hudson at second, singled with two outs. Branyan fielded Webb's high chopper at third but threw wildly past first.