A first half penalty from Daniel Johnson gave the Lilywhites the points in a pulsating game at Deepdale.
Johnson converted from the spot after Scott Sinclair had been upended by Tyler Moore, but both goalkeepers had to be in superb form to keep the score to 1-0.
Alex Neil made two changes to the side that faced Barnsley in midweek, Andrew Hughes missing out through injury and Emil Riis dropping to the bench, with Sinclair and Paul Huntington coming in, with a switch back to a 4-2-3-1 formation.
And the Lilywhites dominated the first half. Their first chance came on 18 minutes, with a great lay-off by Sean Maguire from Declan Rudd’s long ball, as Sinclair ran off him and sprinted 40 yards before firing left-footed effort via a deflection wide for a corner.
More pressure came from the corner, with Maguire eventually spinning and shooting, saved low by Robins skipper Daniel Bentley.
But North End had the chance to go in front on 20 minutes as Sinclair was threaded through by a brilliant Paul Gallagher pass and the winger was brought down by Tyler Moore – an ex-Blackpool loanee – referee Geoff Eltringham pointing to the spot.
Johnson stepped up and sent Bentley the wrong way, powering the resulting spot kick into the
bottom right-hand corner for his 49th career goal and to give his side a deserved lead.
They had an excellent chance to double the lead in the final seconds of the three added-on minutes at the end of the half, as Tom Barkhuizen raced down the right and delivered a superb cross deep that Sinclair met with a left-footed volley back across goal, but Bentley got down well to save.
The visitors, who had not had an attempt on goal in the first half, made a double change at the break, Dean Holden bringing on Antoine Semenyo and Tyreeq Bakinson for Moore and Han-Noah Massengo.
The changes reinvigorated Bristol City and Declan Rudd needed to be on hand to make an excellent two-handed save to deny Irish international Callum O’Dowda an equaliser 11 minutes into the second period, diving away to his right to push the shot behind for a corner.
A corner for North End, taken by substitute Joe Rafferty with seven minutes left, almost brought a second, as the ball came out, on the bounce, to Alan Browne, who powered a header back through a crowd, but it flashed just wide of the right-hand post.
Two more subs, Emil Riis and Tom Bayliss, combined on the break, with the latter going close with a shot from 15 yards as the game entered the final five minutes, but at the other end a wonderful one-handed save by Rudd tipped over O’Dowda’s header from Semenyo’s cross with just three minutes left on the clock.
At the start of five added-on minutes, Bentley kept his side in the game with a brilliant low stop to deny Riis a goal, after Sinclair had raced away from halfway and threaded the ball to the Dane on his left, only for the keeper to deny him with a superb fingertip save.
Despite some late corners for the visitors, the Lilywhites took the game 1-0.
PNE line-up: Rudd, Fisher (Rafferty, 69), Earl, Browne (c), Huntington, Davies, Barkhuizen, Gallagher (Bayliss, 76), Maguire (Riis, 69), Johnson (Stockley, 80), Sinclair. Subs not used: Ripley, Harrop, Storey, Holland-Wilkinson, O’Reilly.
Bristol City line-up: Bentley, Hunt, Nagy, Martin, O’Dowda, Wells, Kalas (c), Moore (Semenyo, 46), Rowe, Vyner, Massengo (Bakinson, 46). Subs not used: O’Leary, Mariappa, Smith, Edwards, Pearson, Bell, Towler.
Referee: Mr G Eltringham