I am going to show you how to make a simple web server in PowerShell that serves the contents of the current directory on any port you choose. You will have optional Active Directory integration so that the HTTP requests sent from the browser respect the NTFS file permissions.

System.Net.HttpListener

System.Net.HttpListener is a powerful and generally underused class introduced to .NET in .NET Framework 2.0 and is still included in the current version of .NET. Thus, our web server is going to run on any system that has PowerShell and at least .NET 2.0. This means we can run it on most Windows server and client operating systems without having to install anything or download any libraries to support our code.

Let's create the listener to get started:

$listener = New-Object System.Net.HttpListener

Configuring the HttpListener

The first thing to do when creating an HttpListener is tell our web server where it should listen. Should it listen and respond to requests coming from other PCs on the network or only requests coming from the localhost? What port should it listen on? What name should it respond to? We answer all of that using a single line of code:

$listener.Prefixes.Add("http://localhost:8080/")

This single line of code is deceptively simple. Setting up prefixes can be the most complex process of creating a self-hosted application. Please read this article that explains UrlPrefix formatting. I recommend using a localhost prefix during development as this does not require any special setup to run and does not need administrative rights. Let's just leave that line as it is for now, and we will get back to setting this up to listen for requests coming from the network at the end of this article. For now, all we need to do is start the listener.

$listener.Start()

Preventing directory traversal

We are going to serve files out of the current working directory in PowerShell, and we want to restrict access to only the files and folders in this directory. We can easily prevent the requester using the \..\..\.. notation in a URL to perform directory traversal attacks. We do this by creating a PSDrive from the current directory and using this as the root file path when our code is looking for requested files.

New-PSDrive -Name MyPowerShellSite -PSProvider FileSystem -Root $PWD.Path

Accessing the request with the HttpListenerContext object

Alright, so we created a listener, we told it where to listen, and we started it. If you were curious you might have opened a web browser at this point to the URL given above, just to see what happens. I'm sorry to disappoint you, but we have one more step left before our listener will begin working with a web browser.

We must tell our listener to get an HttpListenerContext object that gives us access to request and response objects. We use the getContext() method on the $listener to do this synchronously. HttpListener does support asynchronous calls, but that is beyond the scope of this article.

$Context = $listener.GetContext()

The console should hang waiting for an HTTP request to hit the URL it is listening on. Going to http://localhost:8080/ using any web browser should populate our $Context variable, and the browser will begin to spin waiting for a response.

The context variable

Inspection of the context variable reveals three properties:

  • Request: This contains information that came from the browser or HTTP client (URL requested, cookies, query string, HTTP method, etc.).
  • Response: This automatically created object will send a response back to the browser.
  • User: Setting an authentication method on the HttpListener would populate this with details about the user (such as username and password or a Windows Identity object).

You can read more details about the HttpListenerContext .NET Class on MSDN. This .NET class can do a lot, but we will focus on the simple stuff here. All we need to know is what file the browser is requesting and send it back using the response.

Reading the request with getContext()

A URL property stores the details of the URL that the web browser or HTTP client requested. For example, if I browse to http://localhost:8080/MyFile.txt and then run the getContext() method, I can access the URL with $Context.Request.Url:

Reading the requested URL

Reading the requested URL

Sending the HTTP response

The LocalPath property looks like it has exactly the information we need. Let's go ahead and get that file and send it back to the web browser.

$URL = $Context.Request.Url.LocalPath
$Content = Get-Content -Encoding Byte -Path "MyPowerShellSite:$URL"
$Context.Response.ContentType = [System.Web.MimeMapping]::GetMimeMapping("MyPowerShellSite:$URL")
$Context.Response.OutputStream.Write($Content, 0, $Content.Length)
$Context.Response.Close()

The MimeMapping method detects the file type and converts it to a file type the browser understands. You should see the contents of your text file exposed in the browser now!

Wrap-up

This is of course a very simplistic example and only serves a single request. A more detailed example with directory browsing showcases how to use integrated Windows authentication and send back errors to a browser in this Github gist.

If you would like a ready-to-go self-hosted PowerShell web server solution written with HttpListener, you can check out the following projects:

Subscribe to 4sysops newsletter!

To set it up on something other than localhost with HTTPS and without running as admin, I recommend this article.

avataravataravatar
5 Comments
  1. Deb 6 years ago

    Hi Micah,

    Could you please guide me to write the script for  “To Test remote web server SSL certificate”

    Regards
    Dev

  2. Raghu Ram 3 years ago

    Hi Mike, Is there a way we can send app.js file as well ?

    My HTML code has app.js in the script field, I am wondering – how can we send this back to the client when they request for it ? Can you advice please ?

  3. Utsav 3 years ago

    Hi Raghu,

     Please share the solution of your question if you did find any.

  4. Mike 3 years ago

    Hi

    I try to bind it on the local ethernet by the system denied it, I know that can be insecure, but I need to open it for my intranet. Can you please guide me, I am so new 🙁

    • Mike 3 years ago

      Hi
      I found my solution
      netsh http add urlacl url=http://machine:80/MyUri user=DOMAIN\USER

      Thank you

Leave a reply

Your email address will not be published. Required fields are marked *

*

© 4sysops 2006 - 2023

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account