A lot of developers work on writing or debugging web applications daily. Sometimes that work involves writing our application’s backend and sometimes consuming other web resources, whether they are APIs or “normal” web pages. Other programming languages recognised the need for having a “quick and dirty” way of serving resources over HTTP(S) protocol during the development. Good people that developed JDK 18 gave us the opportunity to have that in Java too so we don’t have to look at node.js, Python or PHP developers with envy any more when they brag how simple it is to start serving resources in their languages. Let’s see the basic usage of Simple Web Server (SWS) in action!

Two faces of SWS

Not only we can use SWS in Java, but we can do so in two flavours: as a new CLI tool jwebserver, or programmatically as a proper API. This article covers the first usage. Programmatic one is covered in part 2.


Start SWS by typing jwebserver in the shell/terminal. You should see output similar to this one:

1$ jwebserver
2Binding to loopback by default. For all interfaces use "-b" or "-b ::".
3Serving /Users/ivanmilosavljevic/Documents/swsdemo and subdirectories on port 8000

It informs us that it uses loopback interface and that it listens on port 8000. We can immediately visit http://localhost:8000 in the browser and we’ll see a directory listing of a directory in which we’ve started SWS. Neat! At the same time, in the terminal we’ll see a log that contains information about the remote host address, date and time of a request, HTTP method, requested resource, version and status code:

1127.0.0.1 - - [30/Mar/2022:19:39:27 +0200] "GET / HTTP/1.1" 200 -

There are two things to note here. First one is, if we have an index.html file in the directory in which we’ve started our server, then contents of that file will be served and not the directory listing. Second one is the visibility of the server - if you start it with -b it will bind to all interfaces and will be accessible from all hosts on your network. Depending on your use-case, this might be either a desired behaviour or a totally unacceptable one.

Of course, we can’t start another instance of SWS on the same port even if we start it from a different directory. If we try, SWS will greet us with:

1$ jwebserver
2Error: server config failed: java.net.BindException: Address already in use

The solution is to change the port it listens to by using --port option:

1$ jwebserver --port 8001
2Binding to loopback by default. For all interfaces use "-b" or "-b ::".
3Serving /Users/ivanmilosavljevic/Documents/anotherdir and subdirectories on port 8001

If we want to serve files from a directory different than the one we start SWS from, we must add a --directory option:

1$ jwebserver --directory ~/tmp
2Binding to loopback by default. For all interfaces use "-b" or "-b ::".
3Serving /Users/ivanmilosavljevic/tmp and subdirectories on port 8000

Various options can be used together of course, like jwebserver --port 8001 --directory ~/tmp. We can stop a running SWS with the usual Ctrl+C or by sending it a SIGINT signal. We can see all supported options by typing jwebserver --help.

Gotchas and caveats

While the normal usage of SWS from command line is very straightforward, there are a few details you should take care of:

  • it only serves single directory hierarchy and only static files
  • only HTTP/1.1 is supported, no HTTPS, HTTP/2 or HTTP/3
  • only GET and HEAD requests are served, everything else will receive either 405 or 501
  • MIME types are automatically assigned
  • symlinks and hidden files are not served

Official JEP specification

I’ll cover programmatic usage of SWS in part 2. In the meantime, if you want to dive deeply into specification of this feature, you may find it in JEP 408.

Dear fellow developer, thank you for reading this article about simple web server in Java. Until next time, TheJavaGuy saluts you!