Swansea City suffered back-to-back defeats for the first time this season as Aden Flint’s early header settled the south Wales derby.
The centre-back nodded in from close range in the eighth minute and, despite heavy pressure from the hosts for much of the final hour, the hosts could not find a leveller.
Andre Ayew went closest to an equaliser, as he glanced a Connor Roberts corner against the far post in the second half, while Morgan Whittaker was twice denied by keeper Dillon Phillips.
The Swans showed four changes from the midweek defeat at Bournemouth, with Whittaker getting his first start as Marc Guehi, Jake Bidwell and Jay Fulton also came into the XI.
The hosts wasted little time testing Bluebirds goalkeeper Phillips, as he tipped a low Conor Hourihane free-kick wide at the near post inside five minutes.
But it was the visitors who took the lead through Flint in the eighth minute.
The Swans failed to clear a long throw from Will Vaulks and, while Freddie Woodman saved the initial effort, he could do nothing about the centre-back’s follow-up.
It was the first goal Swansea had conceded in a derby since November 2013, and Woodman had to be alert to prevent Leandro Bacuna volleying in a second from a searching Vaulks pass.
Hourihane had picked up a knock in a challenge with Bacuna and he was forced off in just the 18th minute, with Korey Smith taking his place.
Swansea were having plenty of the ball, but were finding it hard to break down a visiting side who were happy to sit deep and look to counter.
Ayew saw a shot blocked from the edge of the box, and the Swans began to stress the Cardiff defence, with Fulton sending a curling effort wide of the far post after good build-up play by Guehi.
Matt Grimes saw a fierce strike blocked after Roberts had rolled a corner to the edge of the box.
The Swans continued to ramp up the pressure as half-time neared, but then received a huge let-off when Woodman gifted the ball to Kieffer Moore with a loose pass.
The striker got away from the keeper, but his final shot lacked power and Ryan Bennett recovered to clear to safety.
Back at the other end, Roberts saw a stinging drive parried by Phillips from a narrow angle, before Whittaker created a big chance for Fulton right on half-time.
However, the midfielder could not gather his feet to turn, and the arriving Ayew saw his follow-up blocked.
Swansea’s pressure continued at the start of the second half, Whittaker having a shot held by Phillips after some smart work by Smith down the right for the Swans.
Ayew then curled a free-kick narrowly wide after being fouled as he tussles with a trio of blue jerseys, and Whittaker then sliced when the Ghanaian’s clever flick had put him in on goal ahead of marker Flint.
Swansea were finding pockets of space around the Cardiff area, but could not quite find the final ball or touch to get themselves back on level terms.
Cardiff’s sole threat came via set-pieces or counter-attacks, and Woodman made a fine save as Harry Wilson tried to find the far corner from one of the latter.
Play soon returned to the other end of the pitch, with a stretching Sean Morrison heading just over his own bar, before Ayew’s flicked header from the resulting corner struck the far post and was cleared.
Cooper sent on Lowe and Manning for Kyle Naughton and Bidwell as Swansea continued to search for an equaliser.
Smith then blazed over after a quick corner had caught the visitors by surprise and one final chance fell to Whittaker, but he steered narrowly wide.
Swansea City: Freddie Woodman; Kyle Naughton (Jamal Lowe 76), Ryan Bennett, Marc Guehi; Connor Roberts, Jay Fulton (Yan Dhanda 80), Matt Grimes (captain), Conor Hourihane (Korey Smith 18), Jake Bidwell (Ryan Manning 76); Morgan Whittaker, Andre Ayew.
Unused Substitutes: Ben Hamer, Ryan Manning, Ben Cabango, Joel Latibeaudiere, Wayne Routledge, Ollie Cooper.
Cardiff City: Dillon Phillips; Sean Morrison (captain), Aden Flint, Ciaron Brown; Thomas Sang, Leandro Bacuna (Josh Murphy 66), Will Vaulks, Marlon Pack, Joe Ralls; Harry Wilson (Sheyi Ojo 84); Kieffer Moore.
Unused Substitutes: Alex Smithies, Jonny Williams, Max Watters, Mark Harris, Junior Hoilett, Rubin Colwill, Sam Bowen.
Referee: Paul Tierney