DEV Community

Vahid Aghajani
Vahid Aghajani

Posted on Originally published at software-engineer-blog.com

WMS vs WMTS vs WFS: A Picture, Fast Tiles, or the Raw Data (and When to Use Each)

Originally published on my blog. Cross-posted here with a canonical link.

Prefer audio? Telegram

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": { "type": "Polygon", "coordinates": [[[828100, 5931200], "…"]] },
      "properties": { "id": 42, "area_m2": 613, "land_use": "residential" }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)