diff --git a/src/main/java/info/textgrid/services/aggregator/html/HTML.java b/src/main/java/info/textgrid/services/aggregator/html/HTML.java
index 2407888a0d9f0707a45da60b10d07e080c5aa11a..80258158d233ea4306c9fe335d9b4bf05ba96426 100644
--- a/src/main/java/info/textgrid/services/aggregator/html/HTML.java
+++ b/src/main/java/info/textgrid/services/aggregator/html/HTML.java
@@ -28,7 +28,8 @@
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.Context;
-import javax.ws.rs.core.StreamingOutput;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
 import javax.xml.transform.stream.StreamSource;
 
 import net.sf.saxon.s9api.Processor;
@@ -199,7 +200,7 @@ private static boolean isPublic(final ObjectType metadata) {
 	@GET
 	@Path(value = "/{object}")
 	@Produces(value = "text/xml")
-	public StreamingOutput get(
+	public Response get(
 			@Description("The TextGrid URI of the TEI document or aggregation to transform")
 			@PathParam("object") final URI uri,
 			@Description("If given, an alternative XSLT stylesheet to use")
@@ -211,17 +212,17 @@ public StreamingOutput get(
 			@QueryParam("sid") final String sid,
 			@Description("If true, pass the information the stylesheet that its result will be embedded into some website") @QueryParam("embedded") final boolean embedded,
 			@Description("URL of the CSS that should be referenced in the HTML that is created")
-			@QueryParam("css") final URI css) throws ObjectNotFoundFault,
+			@QueryParam("css") final URI css,
+			@Context final Request request) throws ObjectNotFoundFault,
 			MetadataParseFault, IoFault, AuthFault,
 			ProtocolNotImplementedFault, WebApplicationException, IOException,
 			SaxonApiException, ExecutionException {
 		logger.fine("HTML called for root object: " + uri);
 
+
 		final HTMLWriter writer = new HTMLWriter(this, uri, xsluri,
-				refreshStylesheet, pi, embedded, css, sid);
-		writer.loadSource();
-		writer.loadStylesheet();
-		return writer;
+				refreshStylesheet, pi, embedded, css, sid, request);
+		return writer.createResponse().build();
 	}
 
 }
diff --git a/src/main/java/info/textgrid/services/aggregator/html/HTMLWriter.java b/src/main/java/info/textgrid/services/aggregator/html/HTMLWriter.java
index 0b6241a78d742be0dc3f3f06faa019e15c103c15..ea63cd990e2140e69033e52b1172dfa54143d6a1 100644
--- a/src/main/java/info/textgrid/services/aggregator/html/HTMLWriter.java
+++ b/src/main/java/info/textgrid/services/aggregator/html/HTMLWriter.java
@@ -10,6 +10,7 @@
 import info.textgrid.services.aggregator.ITextGridRep;
 import info.textgrid.services.aggregator.ITextGridRep.TGOSupplier;
 import info.textgrid.services.aggregator.teicorpus.TEICorpusSerializer;
+import info.textgrid.utils.export.filenames.DefaultFilenamePolicy;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -21,6 +22,11 @@
 import java.util.logging.Logger;
 
 import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.CacheControl;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
 import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.StreamingOutput;
 import javax.xml.transform.Source;
@@ -111,6 +117,10 @@ private enum SourceType {
 
 	private String actualStylesheet;
 
+	private TGOSupplier<InputStream> content;
+
+	private Request request;
+
 	// Constructor and configuration
 
 	public HTMLWriter(final HTML service, final URI rootURI) {
@@ -125,7 +135,7 @@ public HTMLWriter(final HTML service, final URI rootURI) {
 	public HTMLWriter(final HTML service, final URI rootURI,
 			final URI stylesheetURI, final boolean refreshStylesheet,
 			final boolean readStylesheetPI, final boolean embedded,
-			final URI css, final String sid) {
+			final URI css, final String sid, final Request request) {
 
 		this(service, rootURI);
 
@@ -135,6 +145,7 @@ public HTMLWriter(final HTML service, final URI rootURI,
 		this.embedded = embedded;
 		this.css = Optional.fromNullable(css);
 		this.sid(sid);
+		this.request = request;
 	}
 
 	public HTMLWriter sid(final String sid) {
@@ -173,9 +184,7 @@ protected HTMLWriter loadSource() throws ObjectNotFoundFault,
 			MetadataParseFault,
 			IoFault, ProtocolNotImplementedFault, AuthFault, IOException {
 
-		TGOSupplier<InputStream> content = null;
-		content = service.repository.read(rootURI, sid.orNull());
-		metadata = content.getMetadata();
+		metadata = getContent().getMetadata();
 		final String format = metadata.getGeneric().getProvided().getFormat();
 		if (format.contains("aggregation")) {
 			sourceType = SourceType.AGGREGATION;
@@ -202,11 +211,11 @@ protected HTMLWriter loadSource() throws ObjectNotFoundFault,
 		} else if (sourceType == SourceType.XML && readStylesheetPI) {
 			final FileBackedOutputStream xmlBuffer = new FileBackedOutputStream(
 					1024 * 1024, true);
-			ByteStreams.copy(content, xmlBuffer);
+			ByteStreams.copy(getContent(), xmlBuffer);
 			detectEmbeddedStylesheet(xmlBuffer.getSupplier().getInput());
 			this.source = new StreamSource(xmlBuffer.getSupplier().getInput());
 		} else {
-			this.source = new StreamSource(content.getInput(),
+			this.source = new StreamSource(getContent().getInput(),
 					rootURI.toString());
 		}
 		logger.log(Level.INFO, MessageFormat.format("Fetched source for {0}, type={1}, after {2}", rootURI, sourceType, stopwatch.toString()));
@@ -214,6 +223,14 @@ protected HTMLWriter loadSource() throws ObjectNotFoundFault,
 		return this;
 	}
 
+	private TGOSupplier<InputStream> getContent() {
+		if (content == null) {
+			content = service.repository.read(rootURI, sid.orNull());
+			logger.info(MessageFormat.format("Fetched source for {0} up to metadata after {1}", rootURI, stopwatch));
+		}
+		return content;
+	}
+
 	private void detectEmbeddedStylesheet(final InputStream input) {
 		try {
 			final Source associatedStylesheet = TransformerFactory
@@ -302,4 +319,30 @@ public void write(final OutputStream out) throws IOException,
 		stopwatch.stop();
 	}
 
+	public ResponseBuilder createResponse() throws ObjectNotFoundFault, MetadataParseFault, IoFault, ProtocolNotImplementedFault, AuthFault, IOException, SaxonApiException {
+		if (!refreshStylesheet && request != null) {
+			final ResponseBuilder builder = request.evaluatePreconditions(getContent().getMetadata().getGeneric().getGenerated().getLastModified().toGregorianCalendar().getTime());
+			if (builder != null) {
+				logger.info(MessageFormat.format("Aborting: client already has HTML for {0} (after {1})", rootURI, stopwatch));
+				return builder;
+			} else {
+				logger.info("Creating new transformation.");
+			}
+		}
+
+		loadSource();
+		loadStylesheet();
+
+		final ResponseBuilder builder = Response.ok();
+		builder.type(MediaType.TEXT_XML_TYPE);
+		builder.lastModified(getContent().getMetadata().getGeneric().getGenerated().getLastModified().toGregorianCalendar().getTime());
+		final CacheControl cacheControl = new CacheControl();
+		cacheControl.setPrivate(sid.isPresent());
+		cacheControl.setMaxAge(86400);	// one day
+		builder.cacheControl(cacheControl);
+		builder.header("Content-Disposition", "inline;filename=\"" + DefaultFilenamePolicy.INSTANCE.getFilename(getContent().getMetadata(), false) + ".html\"");
+		builder.entity(this);
+		return builder;
+	}
+
 }