Coventry, meanwhile, suffered a humbling 2-0 reverse to already-relegated Doncaster Rovers. A 0-0 draw appeared to be on the cards until the last 10 minutes, with James Hayter converting an 81st minute penalty following Richard Keogh's red card and Simon Gillett netting just before the close.
Bristol City defeated Barnsley 2-0, Cole Skuse and Jon Stead (penalty) on target as they ensured Championship football for another year.
The result leaves Tony Mowbray's side two points behind sixth-placed Cardiff with one game to play, while Southampton remain second, needing a win in their last game to guarantee promotion.
Birmingham picked up the point they needed to secure their place in the Championship play-offs despite a late rally from Brighton in a 1-1 draw. Substitute Nathan Redmond put the visitors ahead in the 69th minute but Ashley Barnes grabbed the leveller for Albion in the closing stages.
Cardiff dropped to sixth after a 1-1 home draw against Leeds in the early kick-off. Joe Mason's opener was cancelled out by Lucciano Becchio in the 73rd minute.
Hull moved into seventh with a 2-1 win over Nottingham Forest but cannot catch the top six. A Chris Gunter own-goal and Matty Fryatt's penalty were enough to see off Forest, whose consolation came via Radoslaw Majewski in injury-time.
Already-promoted Reading drew 2-2 with Crystal Palace. The Royals had Jason Roberts sent off on the stroke of half-time with the score at 1-1 after Wilfried Zaha's 14th-minute strike and Kaspars Gorkss's swift equaliser. Reading went ahead through Adam Le Fondre but were pegged back by Glenn Murray 16 minutes from time.
An Andy Keogh brace and a Liam Trotter goal scored Millwall a 3-0 victory at Ipswich, while Peterborough drew 2-2 with visiting Watford. Lee Tomlin gave Posh the lead with a 14th-minute spot-kick only for Troy Deeney (26) and Sean Murray (48) to turn the tables. Paul Taylor rescued the point in the 54th minute.