---
created_at: '2013-08-16T04:46:04.000Z'
title: Webserver in bash (2007)
url: http://paulbuchheit.blogspot.ca/2007/04/webserver-in-bash.html
author: soundsop
points: 53
story_text: ''
comment_text: 
num_comments: 14
story_id: 
story_title: 
story_url: 
parent_id: 
created_at_i: 1376628364
_tags:
- story
- author_soundsop
- story_6222215
objectID: '6222215'

---
[Source](http://paulbuchheit.blogspot.ca/2007/04/webserver-in-bash.html "Permalink to Paul Buchheit: Webserver in bash")

# Paul Buchheit: Webserver in bash

#  [ Paul Buchheit ][1]

## Friday, April 13, 2007

###  Webserver in bash 

And not using perl or any of that fancy stuff. It's the inane things that keep me awake at night.  
  
Getting nc to behave turned out to be the most difficult part. It won't exit until both ends of the connection are closed. Correction, making blogger not mangle this code was the most difficult part.  
  

#!/bin/bash  
# web.sh -- <http://localhost:9000/hello?world>  
  
RESP=/tmp/webresp  
[ -p $RESP ] || mkfifo $RESP  
  
while true ; do  
( cat $RESP ) | nc -l -p 9000 | (  
REQ=`while read L && [ " " "<" "$L" ] ; do echo "$L" ; done`  
echo "[`date '+%Y-%m-%d %H:%M:%S'`] $REQ" | head -1  
cat >$RESP <<EOF  
HTTP/1.0 200 OK  
Cache-Control: private  
Content-Type: text/plain  
Server: bash/2.0  
Connection: Close  
Content-Length: ${#REQ}  
  
$REQ  
EOF  
)  
done  

  
Update: Fixed script so that it also work in Linux, where tr lacks the -u option. 

Posted by  Paul Buchheit at  [5:53 PM][2] [ ![][3] ][4]

#### 6 comments:

![][5][ ][6]

[abbot][6] said... 
: 

It should be like this to behave with both gnu and bsd utils:  
( cat $RESP ) | nc -l 9000 | sed -ue 's/r//g' | ( 

:  [ 2:29 PM ][7] [ ![][8] ][9]

![][10][ ][11]

[Paul Buchheit][11] said... 
: 

Thanks abbot, but OSX sed doesn't support -u. I found a way to get rid of tr instead. 

:  [ 3:53 PM ][12] [ ![][8] ][13]

![][14][ ][15]

[brett][15] said... 
: 

well that wasted a bit of my day.  
how about a version that takes a document root and serves html files:  
  
#!/bin/bash  
# pass in document root as first arg  
# web.sh ~/Sites/test   
DOC_ROOT=$1  
RESP=/tmp/webresp  
[ -p $RESP ] || mkfifo $RESP  
  
while true ; do  
( cat $RESP ) | nc -l -p 9000 | tr -ud 'r' | (  
REQ=`while read L && [ -n "$L" ] ; do echo "$L" ; done`  
SERV_PATH=`echo "$REQ" | head -1 | cut -f2 -d" " `  
FULL_PATH="$DOC_ROOT$SERV_PATH"  
echo $FULL_PATH  
if [ -f "$FULL_PATH" ]; then  
RESP_BODY=`cat $FULL_PATH`  
RESP_CODE="200 OK"  
else  
RESP_BODY="not found<br/><br/>$REQ"  
RESP_CODE="404 NOT FOUND"  
fi  
echo "[`date '+%Y-%m-%d %H:%M:%S'`] $REQ" | head -1  
cat >$RESP <<EOF  
HTTP/1.0 $RESP_CODE  
Cache-Control: private  
Content-Type: text/html  
Server: bash/2.0  
Connection: Close  
Content-Length: ${#RESP_BODY}  
  
$RESP_BODY  
EOF  
)  
done 

:  [ 11:00 PM ][16] [ ![][8] ][17]

![][18][ ][19]

[andreas][19] said... 
: 

Another web server written in bash: http://sourceforge.net/projects/mws/ 

:  [ 6:23 AM ][20] [ ![][8] ][21]

![][22][ ][23]

[Bill Blum][23] said... 
: 

This both intrigues me and scares me... 

:  [ 7:34 AM ][24] [ ![][8] ][25]

![][26][ ][27]

[ralph][27] said... 
: 

Does the HTTP spec. say the time must be GMT rather than your local timezone, e.g. "TZ=GMT date ...".  
  
Cheers, Ralph Corderoy. 

:  [ 9:46 AM ][28] [ ![][8] ][29]

[Post a Comment][30]

[Newer Post][31] [Older Post][32] [Home][1]

Subscribe to: [Post Comments (Atom)][33]

## About Me

[ Paul Buchheit ][11]
: <http://en.wikipedia.org/wiki/Paul_Buchheit> <https://twitter.com/paultoo>

[View my complete profile][11]

[ ![][34] ][35]

## Blog Archive

* [ ►  ][36] [ 2014 ][37] (2)
    * [ ►  ][36] [ July ][38] (1)
    * [ ►  ][36] [ March ][39] (1)
* [ ►  ][36] [ 2012 ][40] (2)
    * [ ►  ][36] [ December ][41] (1)
    * [ ►  ][36] [ March ][42] (1)
* [ ►  ][36] [ 2011 ][43] (3)
    * [ ►  ][36] [ August ][44] (1)
    * [ ►  ][36] [ February ][45] (1)
    * [ ►  ][36] [ January ][46] (1)
* [ ►  ][36] [ 2010 ][47] (10)
    * [ ►  ][36] [ December ][48] (2)
    * [ ►  ][36] [ October ][49] (2)
    * [ ►  ][36] [ April ][50] (1)
    * [ ►  ][36] [ February ][51] (1)
    * [ ►  ][36] [ January ][52] (4)
* [ ►  ][36] [ 2009 ][53] (11)
    * [ ►  ][36] [ December ][54] (1)
    * [ ►  ][36] [ November ][55] (2)
    * [ ►  ][36] [ October ][56] (1)
    * [ ►  ][36] [ September ][57] (2)
    * [ ►  ][36] [ April ][58] (1)
    * [ ►  ][36] [ January ][59] (4)
* [ ►  ][36] [ 2008 ][60] (12)
    * [ ►  ][36] [ December ][61] (2)
    * [ ►  ][36] [ April ][62] (2)
    * [ ►  ][36] [ March ][63] (3)
    * [ ►  ][36] [ February ][64] (2)
    * [ ►  ][36] [ January ][65] (3)
* [ ▼  ][36] [ 2007 ][66] (49)
    * [ ►  ][36] [ December ][67] (4)
    * [ ►  ][36] [ November ][68] (2)
    * [ ►  ][36] [ October ][69] (1)
    * [ ►  ][36] [ September ][70] (1)
    * [ ►  ][36] [ August ][71] (4)
    * [ ►  ][36] [ June ][72] (15)
    * [ ►  ][36] [ May ][73] (4)
    * [ ▼  ][36] [ April ][74] (10)
        * [And you thought all the good domain names were tak...][75]
        * [Whose reality are you living in? Whose reality wou...][76]
        * [The secret to making things easy: avoid hard probl...][77]
        * [The problem with conventional databases][78]
        * [What if I could not only change the way things are...][79]
        * [And there it is. Microsoft complains about Google ...][80]
        * [1 - 2 + 3 - 4 + 5 - 6 + ... made easy][81]
        * [Google buys DoubleClick, for a double-dose of the ...][82]
        * "[Perfect" is the enemy of "good enough", and][83]
        * [Webserver in bash][84]
    * [ ►  ][36] [ March ][85] (8)

[ ![][34] ][86]

| -----|  
|  

  |  

  |  

Awesome Inc. theme. Powered by [Blogger][87]. 

[ ![][34] ][88]

[1]: http://paulbuchheit.blogspot.ca/
[2]: http://paulbuchheit.blogspot.ca/2007/04/webserver-in-bash.html "permanent link"
[3]: https://resources.blogblog.com/img/icon18_edit_allbkg.gif
[4]: https://www.blogger.com/post-edit.g?blogID=33042626&postID=562274654513330171&from=pencil "Edit Post"
[5]: http://lh3.googleusercontent.com/zFdxGE77vvD2w5xHy6jkVuElKv-U9_9qLkRYK8OnbDeJPtjSZ82UPq5w6hJ-SA%3Ds35 "abbot"
[6]: https://www.blogger.com/profile/12787554682044686529
[7]: http://paulbuchheit.blogspot.com/2007/04/webserver-in-bash.html?showComment=1176586140000#c2779793812137913370 "comment permalink"
[8]: https://resources.blogblog.com/img/icon_delete13.gif
[9]: https://www.blogger.com/delete-comment.g?blogID=33042626&postID=2779793812137913370 "Delete Comment"
[10]: http://lh3.googleusercontent.com/zFdxGE77vvD2w5xHy6jkVuElKv-U9_9qLkRYK8OnbDeJPtjSZ82UPq5w6hJ-SA%3Ds35 "Paul Buchheit"
[11]: https://www.blogger.com/profile/08521809827597159995
[12]: http://paulbuchheit.blogspot.com/2007/04/webserver-in-bash.html?showComment=1176591180000#c1844685749139643747 "comment permalink"
[13]: https://www.blogger.com/delete-comment.g?blogID=33042626&postID=1844685749139643747 "Delete Comment"
[14]: http://lh3.googleusercontent.com/zFdxGE77vvD2w5xHy6jkVuElKv-U9_9qLkRYK8OnbDeJPtjSZ82UPq5w6hJ-SA%3Ds35 "brett"
[15]: https://www.blogger.com/profile/04377887782335225057
[16]: http://paulbuchheit.blogspot.com/2007/04/webserver-in-bash.html?showComment=1176616800000#c5201403021617360132 "comment permalink"
[17]: https://www.blogger.com/delete-comment.g?blogID=33042626&postID=5201403021617360132 "Delete Comment"
[18]: http://lh3.googleusercontent.com/zFdxGE77vvD2w5xHy6jkVuElKv-U9_9qLkRYK8OnbDeJPtjSZ82UPq5w6hJ-SA%3Ds35 "andreas"
[19]: https://www.blogger.com/profile/02354528504380421921
[20]: http://paulbuchheit.blogspot.com/2007/04/webserver-in-bash.html?showComment=1176643380000#c4926329429675920766 "comment permalink"
[21]: https://www.blogger.com/delete-comment.g?blogID=33042626&postID=4926329429675920766 "Delete Comment"
[22]: http://lh3.googleusercontent.com/zFdxGE77vvD2w5xHy6jkVuElKv-U9_9qLkRYK8OnbDeJPtjSZ82UPq5w6hJ-SA%3Ds35 "Bill Blum"
[23]: https://www.blogger.com/profile/01038014381331047066
[24]: http://paulbuchheit.blogspot.com/2007/04/webserver-in-bash.html?showComment=1176647640000#c5455316264943000809 "comment permalink"
[25]: https://www.blogger.com/delete-comment.g?blogID=33042626&postID=5455316264943000809 "Delete Comment"
[26]: http://lh3.googleusercontent.com/zFdxGE77vvD2w5xHy6jkVuElKv-U9_9qLkRYK8OnbDeJPtjSZ82UPq5w6hJ-SA%3Ds35 "ralph"
[27]: https://www.blogger.com/profile/13140975971019765573
[28]: http://paulbuchheit.blogspot.com/2007/04/webserver-in-bash.html?showComment=1177519560000#c2662854341468491530 "comment permalink"
[29]: https://www.blogger.com/delete-comment.g?blogID=33042626&postID=2662854341468491530 "Delete Comment"
[30]: https://www.blogger.com/comment.g?blogID=33042626&postID=562274654513330171
[31]: http://paulbuchheit.blogspot.ca/2007/04/perfect-is-enemy-of-good-enough-and.html "Newer Post"
[32]: http://paulbuchheit.blogspot.ca/2007/03/anyone-else-notice-that-techstars-and-y.html "Older Post"
[33]: http://paulbuchheit.blogspot.com/feeds/562274654513330171/comments/default
[34]: https://resources.blogblog.com/img/icon18_wrench_allbkg.png
[35]: //www.blogger.com/rearrange?blogID=33042626&widgetType=Profile&widgetId=Profile1&action=editWidget&sectionId=sidebar-right-1 "Edit"
[36]: javascript:void(0)
[37]: http://paulbuchheit.blogspot.ca/2014/
[38]: http://paulbuchheit.blogspot.ca/2014/07/
[39]: http://paulbuchheit.blogspot.ca/2014/03/
[40]: http://paulbuchheit.blogspot.ca/2012/
[41]: http://paulbuchheit.blogspot.ca/2012/12/
[42]: http://paulbuchheit.blogspot.ca/2012/03/
[43]: http://paulbuchheit.blogspot.ca/2011/
[44]: http://paulbuchheit.blogspot.ca/2011/08/
[45]: http://paulbuchheit.blogspot.ca/2011/02/
[46]: http://paulbuchheit.blogspot.ca/2011/01/
[47]: http://paulbuchheit.blogspot.ca/2010/
[48]: http://paulbuchheit.blogspot.ca/2010/12/
[49]: http://paulbuchheit.blogspot.ca/2010/10/
[50]: http://paulbuchheit.blogspot.ca/2010/04/
[51]: http://paulbuchheit.blogspot.ca/2010/02/
[52]: http://paulbuchheit.blogspot.ca/2010/01/
[53]: http://paulbuchheit.blogspot.ca/2009/
[54]: http://paulbuchheit.blogspot.ca/2009/12/
[55]: http://paulbuchheit.blogspot.ca/2009/11/
[56]: http://paulbuchheit.blogspot.ca/2009/10/
[57]: http://paulbuchheit.blogspot.ca/2009/09/
[58]: http://paulbuchheit.blogspot.ca/2009/04/
[59]: http://paulbuchheit.blogspot.ca/2009/01/
[60]: http://paulbuchheit.blogspot.ca/2008/
[61]: http://paulbuchheit.blogspot.ca/2008/12/
[62]: http://paulbuchheit.blogspot.ca/2008/04/
[63]: http://paulbuchheit.blogspot.ca/2008/03/
[64]: http://paulbuchheit.blogspot.ca/2008/02/
[65]: http://paulbuchheit.blogspot.ca/2008/01/
[66]: http://paulbuchheit.blogspot.ca/2007/
[67]: http://paulbuchheit.blogspot.ca/2007/12/
[68]: http://paulbuchheit.blogspot.ca/2007/11/
[69]: http://paulbuchheit.blogspot.ca/2007/10/
[70]: http://paulbuchheit.blogspot.ca/2007/09/
[71]: http://paulbuchheit.blogspot.ca/2007/08/
[72]: http://paulbuchheit.blogspot.ca/2007/06/
[73]: http://paulbuchheit.blogspot.ca/2007/05/
[74]: http://paulbuchheit.blogspot.ca/2007/04/
[75]: http://paulbuchheit.blogspot.ca/2007/04/and-you-thought-all-good-domain-names.html
[76]: http://paulbuchheit.blogspot.ca/2007/04/whose-reality-are-you-living-in-whose.html
[77]: http://paulbuchheit.blogspot.ca/2007/04/secret-to-making-things-easy-avoid-hard.html
[78]: http://paulbuchheit.blogspot.ca/2007/04/problem-with-conventional-databases.html
[79]: http://paulbuchheit.blogspot.ca/2007/04/what-if-i-could-not-only-change-way.html
[80]: http://paulbuchheit.blogspot.ca/2007/04/and-there-it-is-microsoft-complains.html
[81]: http://paulbuchheit.blogspot.ca/2007/04/1-2-3-4-5-6-made-easy.html
[82]: http://paulbuchheit.blogspot.ca/2007/04/google-buys-doubleclick-for-double-dose.html
[83]: http://paulbuchheit.blogspot.ca/2007/04/perfect-is-enemy-of-good-enough-and.html
[84]: http://paulbuchheit.blogspot.ca/2007/04/webserver-in-bash.html
[85]: http://paulbuchheit.blogspot.ca/2007/03/
[86]: //www.blogger.com/rearrange?blogID=33042626&widgetType=BlogArchive&widgetId=BlogArchive1&action=editWidget&sectionId=sidebar-right-1 "Edit"
[87]: https://www.blogger.com
[88]: //www.blogger.com/rearrange?blogID=33042626&widgetType=Attribution&widgetId=Attribution1&action=editWidget&sectionId=footer-3 "Edit"

  [*[5:53 PM]: 2007-04-13T17:53:00-07:00

