We present a consistent and transparent caching system for dynamic web pages produced by a server-side application using a back-end database. Cached pages always reflect current database values. No intervention from the programmer is necessary to implement caching. The system is an improvement on earlier methods that either did not guarantee consistency and/or relied on substantial programmer intervention. The novel idea is that a compiler analyzes and transforms the server-side application code to include cache checks, inserts, and invalidations. In order to provide precise invalidations and attendant good hit ratios, we check the intersection of the database table columns used by the read and the write queries, augmented by uniqueness information from the database schema and comparison of the query selection predicates against values inserted in the database. We use Java bytecode rewriting to implement the transformation of the server-side application. Using the Rubis benchmark, we demonstrate that transparent and consistent caching achieves substantial improvements in response time and throughput.