In a large stock pot over medium-high heat combine onions, celery, garlic, and butter. Stir until butter is melted. Cook 2-3 minutes, stirring occasionally, until onions are translucent.
Add potatoes, season with salt and pepper, and cook for 5 minutes.
Stir in broth, bring to a low boil and cook for 10 minutes until potatoes begin to get tender.
Stir flour into the milk and gradually add into the soup along with heavy cream or half and half.
Cook for another 5 or so minutes until potatoes are fork-tender.
Transfer in batches to a blender (always be cautious when blending hot liquids!) and puree until smooth. Stir back into the pot.
Stir in cheese until completely melted. Taste, add salt and pepper if needed, and serve immediately garnished with fresh herbs, cracked black pepper, and cooked bacon if desired.