Santi Cazorla scored twice as Villarreal denied Real Madrid a chance to reduce the gap to La Liga leaders Barcelona to five points.
The visitors, who had to contend with a pre-Christmas trip to the Club World Cup, looked lethargic from the start.
Cazorla’s curler earned the hosts an early lead before Karim Benzema’s header made it 1-1 after seven minutes.
Raphael Varane’s header put Real ahead, but Cazorla’s late header moved Villarreal out of the relegation zone.
Santi Solari’s Real remain fourth, seven points behind Barcelona, four behind second-placed Athletico Madrid and two behind third-placed Sevilla.
Real’s night was not improved by Gareth Bale being substituted at half-time with what appeared to be a hamstring injury.
And they were made to pay by a Villarreal team, who merited at least a point for their superior attacking endeavour and a player who showed he still retained his class.