WMS vs WMTS vs WFS: A Picture, Fast Tiles, or the Raw Data (and When to Use Each)
WMS, WMTS, and WFS are three OGC web services that answer the same question — how does map data get from a server into your browser — in three completely different ways. WMS renders a flat PNG picture on the fly (flexible, but just pixels). WMTS is that same picture pre-sliced into cacheable tiles (fast, but rigid). WFS sends the actual vector geometry plus attributes (clickable and queryable, but heavier). Here's the mental model, the trade-offs, and a clear rule for which to reach for.
Open any government geoportal, toggle a few layers, and you'll see three acronyms show up over and over: WMS, WMTS, and WFS. They're all OGC web services, they all serve map data, and they're all three letters apart — so it's easy to assume they're competing versions of the same thing.
They aren't. They answer three different questions about how map data gets from a server into your browser. Get the distinction and a whole class of "why is my map slow / why can't I click this / why does it look wrong at this zoom" problems disappears.
The one-line mental model:
- WMS hands you a picture. You ask for an area, the server renders a flat PNG on the fly and sends the image.
- WMTS is that same picture, pre-sliced into a fixed grid of ready-made tiles the browser just grabs.
- WFS sends the actual data — the real vector geometry of each feature plus its attributes.
Or, the analogy that sticks: WMS is a photo of a spreadsheet, WMTS is that photo cut into a jigsaw so it loads fast, and WFS is the actual spreadsheet you can open and edit.
WMS — the Web Map Service: render me a picture
With WMS you send the server a bounding box, a size, a projection, and a list of layers, and it renders a flat raster image on the fly and hands it back. It's a GetMap request:
GET /geoserver/wms?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap
&LAYERS=parcels
&CRS=EPSG:3857
&BBOX=828000,5931000,830000,5933000
&WIDTH=1024&HEIGHT=1024
&FORMAT=image/png HTTP/1.1
You get back a 1024×1024 PNG of exactly that area, styled however the server (or your STYLES/SLD) says.
The appeal is flexibility: any arbitrary area, any projection, any styling, composited server-side into one image. The catch is twofold. First, it's just pixels — there's no parcel #42 in there you can click, only colored dots. Second, because every request is a bespoke render, it re-renders on every pan and zoom, which puts load on the server and can feel sluggish.
WMTS — the Web Map Tile Service: the same picture, pre-sliced
WMTS serves the same kind of raster image, but instead of rendering an arbitrary box on demand, the whole map is pre-cut into a fixed pyramid of square tiles (typically 256×256) at set zoom levels. Your client just asks for tiles by z/x/y grid coordinates:
GET /geoserver/gwc/service/wmts?SERVICE=WMTS&REQUEST=GetTile
&LAYER=parcels&TILEMATRIXSET=EPSG:3857
&TILEMATRIX=14&TILEROW=5701&TILECOL=8642
&FORMAT=image/png HTTP/1.1
Because the tiles are identical for everyone and align to a fixed grid, they're trivially cacheable — by the browser, a CDN, or a tile cache like GeoWebCache. That's why slippy web maps feel instant: you're grabbing pre-baked images, often from an edge cache, not waiting on a live render.
The trade-off is rigidity. You only get the zoom levels, projection, and styles that were baked ahead of time. Want a custom style or an in-between zoom? It isn't in the cache. WMTS trades WMS's on-the-fly flexibility for speed and scale.
WFS — the Web Feature Service: give me the raw data
WFS doesn't send a picture at all. It sends the actual features — the real vector geometry plus each feature's attributes — usually as GML or GeoJSON. A GetFeature request:
GET /geoserver/wfs?SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature
&TYPENAMES=parcels&OUTPUTFORMAT=application/json
&BBOX=828000,5931000,830000,5933000,EPSG:3857 HTTP/1.1
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": { "type": "Polygon", "coordinates": [[[828100, 5931200], "…"]] },
"properties": { "id": 42, "area_m2": 613, "land_use": "residential" }
}
]
}
Now you have real objects, not pixels. You can click a parcel, read its ID and area, run a query ("all parcels over 500 m²"), measure, edit, and restyle it entirely client-side. WFS-T even lets you write features back.
The cost is weight. Vector geometry with attributes is far heavier than a compressed image, and rendering thousands of features in the browser is more work than dropping a PNG. WFS buys you interactivity and query power at the price of bytes and CPU.
The real differences
| WMS | WMTS | WFS | |
|---|---|---|---|
| What you get | Rendered image (raster) | Rendered image (raster) | Vector features + attributes |
| Rendering | On the fly, per request | Pre-rendered tiles | Client-side (you render) |
| Speed | Moderate (live render) | Fast (cacheable tiles) | Slower (heavier payload) |
| Flexibility | High (any area/style/CRS) | Low (baked grid + styles) | High (query/restyle/edit) |
| Clickable / queryable | No (pixels) | No (pixels) | Yes (real objects) |
| Caching | Hard (bespoke renders) | Trivial (fixed tiles) | Manual |
| Typical use | Custom overlays, print | Fast basemaps | Feature editing, analysis |
The trade-off is bigger than maps
Strip the geospatial labels off and WMS / WMTS / WFS are three answers to a universal serving question: do I compute the result on demand, serve a pre-computed cached artifact, or ship the raw data and let the client assemble it?
That same triangle shows up almost everywhere you serve data at scale:
- Compute on demand (WMS-like) — flexible, always current, but you pay CPU on every request. Think server-side rendering per request, or an LLM generating a fresh answer every time.
- Pre-compute and cache (WMTS-like) — fast and cheap to scale because the work is done once and reused, but rigid and potentially stale. Think a CDN of pre-rendered pages, materialized views, or a semantic/response cache in front of an LLM so repeated questions hit a baked answer instead of a fresh (and costly) generation.
- Ship the raw data (WFS-like) — maximum flexibility for the client, heaviest payload. Think a REST/GraphQL API returning raw records, or RAG returning raw retrieved chunks and letting the model do the assembly.
The lesson transfers cleanly: caching a rendered artifact is fast but rigid; shipping raw data is flexible but heavy; rendering on demand sits in between. Whether it's map tiles or model responses, you're choosing where the work happens and who pays for it.
Which should you reach for?
- Reach for WMTS when you want a fast, scalable basemap and a fixed set of styles/zooms is fine. It's the right default for the background layer everyone loads.
- Reach for WFS when users need to click, query, measure, or edit features, or you need the raw geometry for client-side analysis.
- Reach for WMS when you need flexible on-the-fly rendering — arbitrary areas, custom server-side styling, odd projections, or print-quality composites — and caching isn't the priority.
And like most "vs" arguments, the honest answer is you often use all three: WMTS for the fast basemap, WMS for a custom-styled overlay, and WFS for the one layer users actually interact with. There's no winner — just the right delivery mechanism for each layer.
Want the 90-second visual version? Watch the reel.